package com.example.demo.service.user.impl;

import com.alibaba.fastjson.JSON;
import com.example.demo.entity.CollectionName;
import com.example.demo.entity.Result;
import com.example.demo.entity.base.EnumState;
import com.example.demo.entity.exception.EnumException;
import com.example.demo.entity.exception.EnumResult;
import com.example.demo.entity.user.LoginUser;
import com.example.demo.service.comm.MongodbService;
import com.example.demo.service.user.UserService;
import com.example.demo.utils.JwtUtil;
import com.example.demo.utils.MD5Utils;
import com.example.demo.utils.StringUtil;
import com.example.demo.utils.regex.RegexUtil;
import com.mongodb.client.MongoClient;
import com.mongodb.client.MongoDatabase;
import org.springframework.data.mongodb.core.MongoTemplate;
import org.springframework.data.mongodb.core.query.Criteria;
import org.springframework.data.mongodb.core.query.Query;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.core.ValueOperations;
import org.springframework.security.authentication.AuthenticationProvider;
import org.springframework.security.authentication.BadCredentialsException;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.AuthenticationException;
import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.core.userdetails.UsernameNotFoundException;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.stereotype.Service;
import org.springframework.util.CollectionUtils;
import org.springframework.web.bind.annotation.PostMapping;

import javax.annotation.PostConstruct;
import javax.annotation.Resource;
import java.util.*;

/**
 * @author cj
 */
@Service
public class UserServiceImpl implements UserService, UserDetailsService {

    @Resource
    MongoTemplate mongoTemplate;
    @Resource
    MongodbService mongodbService;
    @Resource
    JwtUtil jwtUtil;
    @Resource
    MongoClient mongoClient;
    MongoDatabase database;
    @Resource
    RedisTemplate<String, String> redisTemplate;
    private String verify = "verify";
    @Resource
    UserService userService;

    @PostConstruct
    public void setDatabase() {
        database = mongoClient.getDatabase ( "demo" );
    }

    /**
     * 账号登录
     */
    @Override
    public Map accountLogin(String account, String password) {
        if (!StringUtil.isEmpty ( account, password )) {
            throw new RuntimeException ( "参数错误：账号或者密码不能未空" );
        } else {
            Query query = Query.query ( Criteria.where ( "account" ).is ( account ) );
            //根据账号查询用户
            Map<String, Object> userMap = mongoTemplate.findOne ( query, Map.class, CollectionName.USER );
            if (userMap == null || userMap.isEmpty ()) {
                throw new RuntimeException ( "查无此人!!!" );
            } else {
                if (Objects.equals ( MD5Utils.getMD5String ( password ), userMap.get ( "password" ).toString () )) {
                    //密码正确，记录token
                    userMap.put ( "_id", userMap.get ( "_id" ).toString () );
                    String token = jwtUtil.sign ( JSON.toJSONString ( userMap ) );
                    userMap.put ( "access-token", token );
                    userMap.put ( "_id", userMap.get ( "_id" ).toString () );
                    userMap.remove ( "password" );
                } else {
                    //密码错误
                    throw new RuntimeException ( "参数错误：账号或者密码错误" );
                }
            }
            UserDetails details = loadUserByUsername ( account );
            return userMap;
        }

    }

    /**
     * 用户注册
     *
     * @param map 注册信息
     * @return 用户信息和token
     */
    @Override
    public Map<String, Object> accountRegister(Map<String, Object> map) {
        if (StringUtil.isEmpty ( map.get ( "verifyCode" ) ) || StringUtil.isEmpty ( map.get ( "account" ) )
                || StringUtil.isEmpty ( map.get ( "password" ) ) || StringUtil.isEmpty ( map.get ( "phone" ) )) {
            throw new RuntimeException ( "参数错误" );
        }
        String verifyCode = map.get ( "verifyCode" ).toString ();
        String password = map.get ( "password" ).toString ();
        map.put ( "password", MD5Utils.getMD5String ( password ) );
        String phone = map.get ( "phone" ).toString ();
        String account = map.get ( "account" ).toString ();
        Criteria criteria = new Criteria ();
        List<Map> list = mongoTemplate.find ( Query.query ( criteria.orOperator ( Criteria.where ( "account" ).is ( account ), Criteria.where ( "phone" ).is ( phone ) ) )
                , Map.class, CollectionName.USER );
        if (!CollectionUtils.isEmpty ( list )) {
            throw new RuntimeException ( "用户已经存在" );
        }
        if (verifyImgYzm ( phone, verifyCode )) {
            map.remove ( "verifyCode" );
            mongoTemplate.insert ( map, CollectionName.USER );
        } else {
            throw new RuntimeException ( "验证码错误" );
        }
        return map;
    }

    /**
     * 修改用户信息
     *
     * @param map 用户信息
     * @return 修改后的用户信息
     */
    @Override
    public Map<String, Object> updateUser(Map<String, Object> map) {
        map.remove ( "type" );
        mongodbService.update ( CollectionName.USER, map );
        return null;
    }

    /**
     * 游客的唯一标识
     *
     * @param ip ip
     * @return 唯一标识
     */
    @Override
    public Map<String, Object> getId(String ip) {
        if (!RegexUtil.ipRegex ( ip )) {
            throw new EnumException ( EnumResult.EX_REQUEST_PAR_CODE );
        }
        List<Map> mapList = mongoTemplate.find ( Query.query ( Criteria.where ( "ip" ).is ( ip ) ), Map.class, CollectionName.USER );
        if (!CollectionUtils.isEmpty ( mapList )) {
            return mapList.get ( 0 );
        } else {
            Map<String, Object> tempMap = new HashMap<> ( 4 );
            tempMap.put ( "ip", ip );
            tempMap.put ( "type", EnumState.STATE_USER_GUEST.getState () );
            return mongoTemplate.insert ( tempMap, CollectionName.USER );
        }
    }

    /**
     * 校验图片验证码
     */
    private boolean verifyImgYzm(String phone, String verifyImgCode) {
        ValueOperations<String, String> opsForValue = redisTemplate.opsForValue ();
        String key = verify + phone;
        boolean hasKey = redisTemplate.hasKey ( key );
        if (hasKey) {
            String s = opsForValue.get ( key );
            assert s != null;
            return s.equalsIgnoreCase ( verifyImgCode );
        }
        return false;
    }

    @Override
    public UserDetails loadUserByUsername(String name) throws UsernameNotFoundException {
        Criteria criteria = new Criteria ();
        List<Map> list = mongoTemplate.find ( Query.query ( criteria.orOperator ( Criteria.where ( "account" ).is ( name ) ) )
                , Map.class, CollectionName.USER );
        if (CollectionUtils.isEmpty ( list )) {
//            throw new RuntimeException( "未找到该用户" );
            return null;
        } else {
            Map map = list.get ( 0 );
            List<String> roles = new ArrayList<> ();
            if ("12345678903".equals ( name )) {
                roles.add ( "test" );
            }
            roles.add ( "demo" );
            map.put ( "permission", roles );
            map.put ( "id", map.get ( "_id" ).toString () );
            LoginUser loginUser = JSON.parseObject ( JSON.toJSONString ( map ), LoginUser.class );
            String token = jwtUtil.sign ( JSON.toJSONString ( map ) );
            loginUser.setAccessToken ( token );
            return loginUser;
        }
    }

}
